/*
 *
 *  * Copyright 2020 New Relic Corporation. All rights reserved.
 *  * SPDX-License-Identifier: Apache-2.0
 *
 */

package com.newrelic.agent.utilization;

import com.newrelic.agent.Agent;
import com.newrelic.agent.MetricNames;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

public class Azure implements CloudVendor {
    static String PROVIDER = "azure";
    private final CloudUtility cloudUtility;

    public Azure(CloudUtility cloudUtility) {
        this.cloudUtility = cloudUtility;
    }

    private static final String INSTANCE_DOCUMENT_URL = "http://169.254.169.254/metadata/instance/compute?api-version=2017-03-01";
    private static final int REQUEST_TIMEOUT_MILLIS = 100;

    // Azure request strings
    private static final String AZURE_VM_ID_REQUEST = "vmId";
    private static final String AZURE_VM_SIZE_REQUEST = "vmSize";
    private static final String AZURE_LOCATION_REQUEST = "location";
    private static final String AZURE_NAME_REQUEST = "name";

    // Azure map keys. These are the keys that will be added to the vendor hash in the JSON generated by the agent.
    private static final String AZURE_VM_ID_KEY = "vmId";
    private static final String AZURE_VM_SIZE_KEY = "vmSize";
    private static final String AZURE_LOCATION_KEY = "location";
    private static final String AZURE_NAME_KEY = "name";

    @Override
    public AzureData getData() {
        try {
            String unparsedResult = getAzureValues();

            JSONParser parser = new JSONParser();
            JSONObject result = null;

            if (unparsedResult != null) {
                result = (JSONObject) parser.parse(unparsedResult);
            }

            // not on Azure
            if (result == null || result.isEmpty()) {
                return AzureData.EMPTY_DATA;
            }

            String location = (String) result.get(AZURE_LOCATION_REQUEST); // "location" value in collection returned from getAzureValues
            String name = (String) result.get(AZURE_NAME_REQUEST); // "name" value in collection returned from getAzureValues
            String vmId = (String) result.get(AZURE_VM_ID_REQUEST); // "vmId" value in collection returned from getAzureValues
            String vmSize = (String) result.get(AZURE_VM_SIZE_REQUEST); // "vmSize" value in collection returned from getAzureValues

            if (cloudUtility.isInvalidValue(location) || cloudUtility.isInvalidValue(name)
                    || cloudUtility.isInvalidValue(vmId) || cloudUtility.isInvalidValue(vmSize)) {
                recordAzureError();
                Agent.LOG.log(Level.WARNING, "Failed to validate Azure value");
                return AzureData.EMPTY_DATA;
            }

            AzureData data = new AzureData(location, name, vmId, vmSize);
            Agent.LOG.log(Level.FINEST, "Found {0}", data);
            return data;
        } catch (Exception e) {
            return AzureData.EMPTY_DATA;
        }
    }

    protected String getAzureValues() {
        try {
            return cloudUtility.httpGet(INSTANCE_DOCUMENT_URL, REQUEST_TIMEOUT_MILLIS, "Metadata:true");
        } catch (Throwable t) {
            Agent.LOG.log(Level.FINEST, MessageFormat.format("Error occurred trying to get Azure values. {0}", t));
            recordAzureError();
        }
        return null;
    }

    private void recordAzureError() {
        cloudUtility.recordError(MetricNames.SUPPORTABILITY_AZURE_ERROR);
    }

    protected static class AzureData implements CloudData {
        private final String location;
        private final String name;
        private final String vmId;
        private final String vmSize;

        static final AzureData EMPTY_DATA = new AzureData();

        private AzureData() {
            location = null;
            name = null;
            vmId = null;
            vmSize = null;
        }

        protected AzureData(String location, String name, String vmId, String vmSize) {
            this.location = location;
            this.name = name;
            this.vmId = vmId;
            this.vmSize = vmSize;
        }

        public String getLocation() {
            return location;
        }

        public String getName() {
            return name;
        }

        public String getVmId() {
            return vmId;
        }

        public String getVmSize() {
            return vmSize;
        }

        @Override
        public Map<String, String> getValueMap() {
            Map<String, String> azure = new HashMap<>();

            if (vmSize == null || vmId == null || location == null || name == null) {
                return azure;
            } else {
                azure.put(AZURE_VM_SIZE_KEY, vmSize);
                azure.put(AZURE_VM_ID_KEY, vmId);
                azure.put(AZURE_LOCATION_KEY, location);
                azure.put(AZURE_NAME_KEY, name);
            }
            return azure;
        }

        @Override
        public String getProvider() {
            return PROVIDER;
        }

        @Override
        public boolean isEmpty() {
            return this == EMPTY_DATA;
        }

        @Override
        public String toString() {
            return "AzureData{" +
                    "location='" + location + '\'' +
                    ", name='" + name + '\'' +
                    ", vmId='" + vmId + '\'' +
                    ", vmSize='" + vmSize + '\'' +
                    '}';
        }
    }

}
